package utils

import (
	"bytes"
	"fmt"
	"os"
	"strings"
	"time"
)

type LogLevel int
type LogOp byte

const (
	LogLV_0 LogLevel = iota
	LogLV_1
	LogLV_2 // DEBG
	LogLV_3 // TRAC
	LogLV_4 // INFO
	LogLV_5 // WARN
	LogLV_6 // EROR
	LogLV_7
)
const (
	LogLV_DEBUG LogLevel = iota + 2
	LogLV_TRACE
	LogLV_INFO
	LogLV_WARN
	LogLV_ERROR
)

const (
	LogOp_ALL LogOp = iota // ALL
	LogOp_EQ               // Equal
	LogOp_GT               // Great >=
	LogOp_LT               // Less <=
)

var (
	logLevelStrings = [...]string{"LV0", "LV1", "DEBG", "TRAC", "INFO", "WARN", "EROR", "LogLV7"}
)

type LogRecord struct {
	Level   LogLevel  // The log level
	Created time.Time // The time at which the log message was created (nanoseconds)
	Source  string    // The message source
	Typestr string    // typestr
	Msg     string    // The message
	Buf     []byte    // The raw
}

/*
1. 如果文件已经存在, 则进行更名
// 移动至utils_file
*/
func checkRotateFile(filename string) error {
	_, err := os.Lstat(filename)
	if err == nil { // file exists
		// Find the next available number
		num := 1
		fname := ""

		// xx.2->xx.3, xx.1 -> xx.2
		num = 100 - 1
		for ; num >= 1; num-- {
			fname = filename + fmt.Sprintf(".%d", num)
			nfname := filename + fmt.Sprintf(".%d", num+1)
			_, err = os.Lstat(fname)
			if err == nil {
				err = os.Rename(fname, nfname)
				if err != nil {
					fmt.Fprintf(os.Stderr, "CheckRotateFile Rename(%s->%s) occur err:%v", fname, nfname, err)

				}
			}
		}

		err = os.Rename(filename, fname)
		if err != nil {
			return fmt.Errorf("CheckRotateFile Rename: %s\n", err)
		}
	}
	return nil
}

type ILogWriter interface {
	LogWrite(rec *LogRecord)
	Close()
}

func FmtLongTime(time time.Time) string {
	zone, _ := time.Zone()
	return fmt.Sprintf("%02d:%02d:%02d %s", time.Hour(), time.Minute(), time.Second(), zone)
}

func FmtShortTime(time time.Time) string {
	return fmt.Sprintf("%02d:%02d", time.Hour(), time.Minute())
}

func FmtLongDate(time time.Time) string {
	return fmt.Sprintf("%04d-%02d-%02d", time.Year(), time.Month(), time.Day())
}

func FmtShortDate(time time.Time) string {
	return fmt.Sprintf("%02d-%02d-%02d", time.Year()%100, time.Month(), time.Day())
}

func FmtELongDate(time time.Time) string {
	return fmt.Sprintf("%04d%02d%02d", time.Year(), time.Month(), time.Day())
}

func FmtEShortDate(time time.Time) string {
	return fmt.Sprintf("%02d%02d%02d", time.Year(), time.Month(), time.Day())
}

func IsFilter(setting_lvl LogLevel, setting_lvlop LogOp, rec *LogRecord) bool {
	switch setting_lvlop {
	case LogOp_ALL:
		return true
	case LogOp_EQ:
		return rec.Level == setting_lvl
	case LogOp_GT:
		return rec.Level >= setting_lvl
	case LogOp_LT:
		return rec.Level <= setting_lvl
	}

	return true
}

func LogLvlStr(lvl LogLevel) string {
	return logLevelStrings[lvl]
}

func LogOperaStr2LogOp(val string) (op LogOp) {
	switch val {
	case "=":
		op = LogOp_EQ
	case "<", "<=":
		op = LogOp_LT
	default:
		op = LogOp_GT
	}
	return
}

func LogLvlStr2Lvl(val string) (lvl LogLevel) {
	switch val {
	case "FINEST":
		lvl = LogLV_0
	case "FINE":
		lvl = LogLV_1
	case "DEBUG":
		lvl = LogLV_DEBUG
	case "TRACE":
		lvl = LogLV_TRACE
	case "INFO":
		lvl = LogLV_INFO
	case "WARNING":
		lvl = LogLV_WARN
	case "ERROR":
		lvl = LogLV_ERROR
	case "CRITICAL":
		lvl = LogLV_ERROR
	default:
		lvl = LogLV_INFO
	}
	return
}

// Known format codes:
// %T - Time (15:04:05 MST)
// %t - Time (15:04)
// %D - Date (2006/01/02)
// %d - Date (01/02/06)
// %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
// %S - Source
// %M - Message
// %P - typestr
// %E - Date (20060102)
// %e - Date (0102)

// Ignores unknown formats
// Recommended: "[%D %T] [%L] (%S) %M"
func FormatLogRecord(format string, rec *LogRecord) []byte {
	if rec == nil {
		return nil
	}
	if len(format) == 0 {
		return nil
	}

	// Split the string into pieces by % signs
	pieces := bytes.Split([]byte(format), []byte{'%'})

	out := bytes.NewBuffer(make([]byte, 0, 64))

	// Iterate over the pieces, replacing known formats
	for i, piece := range pieces {
		if i > 0 && len(piece) > 0 {
			switch piece[0] {
			case 'T':
				out.WriteString(FmtLongTime(rec.Created))
			case 'P':
				out.WriteString(rec.Typestr)
			case 't':
				out.WriteString(FmtShortTime(rec.Created))
			case 'D':
				out.WriteString(FmtLongDate(rec.Created))
			case 'd':
				out.WriteString(FmtShortDate(rec.Created))
			case 'E':
				out.WriteString(FmtELongDate(rec.Created))
			case 'e':
				out.WriteString(FmtEShortDate(rec.Created))
			case 'L':
				s := logLevelStrings[rec.Level]
				out.WriteString(s)
				//out.WriteString(logLevelStrings[cachelist.Level])
			case 'S':
				out.WriteString(rec.Source)
			case 's':
				slice := strings.Split(rec.Source, "/")
				s := slice[len(slice)-1]
				out.WriteString(s)
			case 'M':
				out.WriteString(rec.Msg)
			}
			if len(piece) > 1 {
				out.Write(piece[1:])
			}
		} else if len(piece) > 0 {
			out.Write(piece)
		}
	}
	return out.Bytes()
}
